package org.wlgzs.office_exam_student.dao;

import java.lang.reflect.Array;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.OutParameter;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

/**
*@author song(mejeesong@qq.com)
*2018年1月26日
*代理QueryRunner,实现性能监控等功能.
*/
public class MyQueryRunner{
	private QueryRunner realQR = null;
	private String sql_ = null;
	private Object params_ = null;
	@SuppressWarnings("rawtypes")
	private Class entityClass = null;
	public MyQueryRunner(QueryRunner qr) {
		this.realQR = qr;
	}
	@SuppressWarnings("rawtypes")
	public MyQueryRunner(QueryRunner qr,Class c) {
		this.realQR = qr;
		this.entityClass = c;
	}
	private void setSqlAndParams(String sql,Object params) {
		this.sql_ = sql;
		this.params_ = params;
	}
	public int[] batch(String sql, Object[][] params) throws SQLException {
		setSqlAndParams(sql, params);
        return realQR.batch(sql, params);
    }
	
	public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
		setSqlAndParams(sql, params);
        return realQR.query(sql, rsh,params);
    }
	
	public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
		setSqlAndParams(sql,null);
		return realQR.query(sql, rsh);
	}
	/**
	*@author song(mejeesong@qq.com)
	*@date 2018年1月28日
	*@param sql
	*@param handlerClass
	*@param objects
	*@return
	*@throws SQLException
	*使用简单方法设置ResultSetHandler
	*/
	@SuppressWarnings({"unchecked" })
	public <T> T query(String sql, int handler,Object ...objects) throws SQLException {
		if(handler == BaseDao.BEAN_HANDLER) {
			return (T) query(sql, new BeanHandler<>(entityClass,new HumpRowProcessor(entityClass)),objects);
		}else if(handler == BaseDao.BEAN_LIST_HANDLER) {
			return (T) query(sql, new BeanListHandler<>(entityClass,new HumpRowProcessor(entityClass)),objects);
		}
		throw new SQLException("this method just suppor for param (sql,BaseDao.BEAN_HANDLER or BaseDao.BEAN_LIST_HANDLER),即第二个参数只接受BaseDao.Bean_Handler 或 BaseDao.BEAN_LIST_HANDLER.");
	}
	
	public int update(String sql) throws SQLException {
		setSqlAndParams(sql, null);
        return realQR.update(sql);
    }
	/**
     * Executes the given INSERT, UPDATE, or DELETE SQL statement with
     * a single replacement parameter.  The <code>Connection</code> is
     * retrieved from the <code>DataSource</code> set in the constructor.
     * This <code>Connection</code> must be in auto-commit mode or the
     * update will not be saved.
     *
     * @param sql The SQL statement to execute.
     * @param param The replacement parameter.
     * @throws SQLException if a database access error occurs
     * @return The number of rows updated.
     */
    public int update(String sql, Object param) throws SQLException {
    	setSqlAndParams(sql, param);
        return realQR.update(sql,param);
    }
	
    public int update(String sql, Object... params) throws SQLException {
    	setSqlAndParams(sql, params);
        return realQR.update(sql,params);
    }
    /**
     * Executes the given INSERT SQL without any replacement parameters.
     * The <code>Connection</code> is retrieved from the
     * <code>DataSource</code> set in the constructor.
     * @param <T> The type of object that the handler returns
     * @param sql The SQL statement to execute.
     * @param rsh The handler used to create the result object from
     * the <code>ResultSet</code> of auto-generated keys.
     * @return An object generated by the handler.
     * @throws SQLException if a database access error occurs
     * @since 1.6
     */
    public <T> T insert(String sql, ResultSetHandler<T> rsh) throws SQLException {
    	setSqlAndParams(sql, null);
        return realQR.insert(sql, rsh);
    }
    
    /**
     * Executes the given INSERT SQL statement. The
     * <code>Connection</code> is retrieved from the <code>DataSource</code>
     * set in the constructor.  This <code>Connection</code> must be in
     * auto-commit mode or the insert will not be saved.
     * @param <T> The type of object that the handler returns
     * @param sql The SQL statement to execute.
     * @param rsh The handler used to create the result object from
     * the <code>ResultSet</code> of auto-generated keys.
     * @param params Initializes the PreparedStatement's IN (i.e. '?')
     * @return An object generated by the handler.
     * @throws SQLException if a database access error occurs
     * @since 1.6
     */
    public <T> T insert(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
		setSqlAndParams(sql, params);
		return realQR.insert(sql, rsh, params);
	}

	public <T> T insert(String sql, int rsh, Object... params) throws SQLException {
		if(rsh == BaseDao.BEAN_HANDLER) {
			return (T) insert(sql, new BeanHandler<>(entityClass,new HumpRowProcessor(entityClass)),params);
		}else if(rsh == BaseDao.BEAN_LIST_HANDLER) {
			return (T) insert(sql, new BeanListHandler<>(entityClass,new HumpRowProcessor(entityClass)),params);
		}else{
			throw new SQLException("this method just suppor for param (sql,BaseDao.BEAN_HANDLER or BaseDao.BEAN_LIST_HANDLER),即第二个参数只接受BaseDao.Bean_Handler 或 BaseDao.BEAN_LIST_HANDLER.");
		}
	}
    
    /**
     * Executes the given batch of INSERT SQL statements. The
     * <code>Connection</code> is retrieved from the <code>DataSource</code>
     * set in the constructor.  This <code>Connection</code> must be in
     * auto-commit mode or the insert will not be saved.
     * @param <T> The type of object that the handler returns
     * @param sql The SQL statement to execute.
     * @param rsh The handler used to create the result object from
     * the <code>ResultSet</code> of auto-generated keys.
     * @param params Initializes the PreparedStatement's IN (i.e. '?')
     * @return The result generated by the handler.
     * @throws SQLException if a database access error occurs
     * @since 1.6
     */
    public <T> T insertBatch(String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
    	setSqlAndParams(sql, params);
        return realQR.insertBatch(sql, rsh, params);
    }
    
    /**
     * Execute an SQL statement, including a stored procedure call, which does
     * not return any result sets.
     * Any parameters which are instances of {@link OutParameter} will be
     * registered as OUT parameters.
     * <p>
     * Use this method when invoking a stored procedure with OUT parameters
     * that does not return any result sets.  If you are not invoking a stored
     * procedure, or the stored procedure has no OUT parameters, consider using
     * {@link #update(java.lang.String, java.lang.Object...) }.
     * If the stored procedure returns result sets, use
     * {@link #execute(java.lang.String, org.apache.commons.dbutils.ResultSetHandler, java.lang.Object...) }.
     * <p>
     * The <code>Connection</code> is retrieved from the <code>DataSource</code>
     * set in the constructor.  This <code>Connection</code> must be in
     * auto-commit mode or the update will not be saved.
     *
     * @param sql The SQL statement to execute.
     * @param params Initializes the CallableStatement's parameters (i.e. '?').
     * @throws SQLException if a database access error occurs
     * @return The number of rows updated.
     */
    public int execute(String sql, Object... params) throws SQLException {
    	setSqlAndParams(sql, params);
        return realQR.execute(sql, params);
    }
    
    /**
     * Execute an SQL statement, including a stored procedure call, which
     * returns one or more result sets.
     * Any parameters which are instances of {@link OutParameter} will be
     * registered as OUT parameters.
     * <p>
     * Use this method when: a) running SQL statements that return multiple
     * result sets; b) invoking a stored procedure that return result
     * sets and OUT parameters.  Otherwise you may wish to use
     * {@link #query(java.lang.String, org.apache.commons.dbutils.ResultSetHandler, java.lang.Object...) }
     * (if there are no OUT parameters) or
     * {@link #execute(java.lang.String, java.lang.Object...) }
     * (if there are no result sets).
     *
     * @param <T> The type of object that the handler returns
     * @param sql The SQL to execute.
     * @param rsh The result set handler
     * @param params The query replacement parameters.
     * @return A list of objects generated by the handler
     * @throws SQLException if a database access error occurs
     */
    public <T> List<T> execute(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
    	setSqlAndParams(sql, params);
        return realQR.execute(sql, rsh, params);
    }

    public QueryRunner getRealQR(){
        return realQR;
    }
	@Override
	public String toString() {
		StringBuilder params = new StringBuilder();
		rangeArray(params_, params);
		return "MyQueryRunner [sql_=" + sql_ + ", params_=[" + params.toString() + "] ]";
	}
	private void rangeArray(Object obj,StringBuilder sb) {
		if(obj==null) {
			sb.append("null,");
			return;
		}
		int len = Array.getLength(obj);
		for(int i=0;i<len;i++) {
			Object o = Array.get(obj, i);
			if(o != null) {
				if(o.getClass().isArray()) {
					sb.append("[");
					rangeArray(o, sb);
					sb.append("]");
				}else {
					sb.append(o.toString());
				}
			}else {
				sb.append("null");
			}
			sb.append(",");
		}
	}
	
}
